package org.flowers.unbeaten.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.flowers.unbeaten.base.entity.Menu;
import org.flowers.unbeaten.base.entity.RoleMenu;
import org.flowers.unbeaten.base.entity.UserRole;
import org.flowers.unbeaten.exception.BusinessException;
import org.flowers.unbeaten.exception.code.BaseResponseCode;
import org.flowers.unbeaten.mapper.MenuMapper;
import org.flowers.unbeaten.service.IMenuService;
import org.flowers.unbeaten.service.IRoleMenuService;
import org.flowers.unbeaten.service.IUserRoleService;
import org.flowers.unbeaten.util.HttpSessionUtils;
import org.flowers.unbeaten.util.Id;
import org.flowers.unbeaten.base.vo.MenuVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * <p>
 * 菜单 服务实现类
 * </p>
 *
 * @author RyanWang
 * @since 2021-07-15
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private IUserRoleService userRoleService;

    @Autowired
    private IRoleMenuService roleMenuService;

    @Autowired
    private HttpSessionUtils httpSessionUtils;

    @Override
    public List<Menu> selectAllMenus() {
        List<Menu> menus = menuMapper.selectList(Wrappers.<Menu>lambdaQuery().orderByAsc(Menu::getMenuOrder));
        return menus;
    }

    @Override
    public void add(Menu menu) {
        menu.setMenuId(Id.generateId());
//        verifyMenu(menu);
        menuMapper.insert(menu);
    }

    @Override
    public void edit(Menu menu) {
        if (StringUtils.isEmpty(menu.getMenuId())) {
            throw new BusinessException("id不能为空");
        }
        Menu result = menuMapper.selectById(menu.getMenuId());
        if (Objects.isNull(result)) {
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        }
        // 只有类型变更或者所属菜单变更
        if (result.getMenuType().equals(menu.getMenuType()) || !result.getParentId().equals(menu.getParentId())) {
//            verifyMenu(menu);
        }
        menuMapper.updateById(menu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<String> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        }
        for (String id : ids) {
            // 获取关联userId
            List<String> userIds = getUserIdsByMenuId(id);
            Menu menu = menuMapper.selectById(id);
            if (Objects.isNull(menu)) {
                throw new BusinessException(BaseResponseCode.DATA_ERROR);
            }
            // 获取下一级
            List<Menu> childs = menuMapper.selectList(Wrappers.<Menu>lambdaQuery().eq(Menu::getParentId, id));
            if (!CollectionUtils.isEmpty(childs)) {
                throw new BusinessException(BaseResponseCode.ROLE_PERMISSION_RELATION);
            }
            menuMapper.deleteById(id);
            // 删除和角色关联
            roleMenuService.remove(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getMenuId, id));

            if (!CollectionUtils.isEmpty(userIds)) {
                // 刷新权限
                userIds.parallelStream().forEach(httpSessionUtils::refreshUerId);
            }
        }
    }

    @Override
    public Menu detail(String menuId) {
        if (StringUtils.isEmpty(menuId)) {
            throw new BusinessException(BaseResponseCode.DATA_ERROR);
        }
        return menuMapper.selectById(menuId);
    }

    @Override
    public Set<String> getPermissionsByUserId(String userId) {

        List<Menu> list = getPermissions(userId);
        Set<String> permissions = new HashSet<>();
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        for (Menu menu : list) {
            if (!StringUtils.isEmpty(menu.getMenuPermission())) {
                permissions.add(menu.getMenuPermission());
            }
        }
        return permissions;
    }

    @Override
    public List getUserIdsByMenuId(String menuId) {
        // 根据menuId，获取所有角色ID
        List<Object> roleIds = roleMenuService.listObjs(Wrappers.<RoleMenu>lambdaQuery().select(RoleMenu::getRoleId).eq(RoleMenu::getMenuId, menuId));
        if (!CollectionUtils.isEmpty(roleIds)) {
            // 根据角色ID， 获取关联用户
            return userRoleService.listObjs(Wrappers.<UserRole>lambdaQuery().select(UserRole::getUserId).in(UserRole::getRoleId, roleIds));
        }
        return null;
    }

    /**
     * 获取所有菜单权限
     * @return
     */
    @Override
    public List<MenuVo> getAllTree() {
        List<Menu> list = selectAllMenus();
        return getTree(list, false);
    }

    /**
     * 获取所有菜单权限（不包含按钮）
     * @return
     */
    @Override
    public List<MenuVo> getAllTreeExceptButton() {
        // 获取全部菜单数据
        List<Menu> menus = selectAllMenus();
        List<MenuVo> menuVos = new ArrayList<>();
        // 新增顶级目录是为了方便添加一级目录
        MenuVo menuVo = new MenuVo();
        menuVo.setMenuId("0");
        menuVo.setMenuName("默认顶级菜单");
        menuVo.setSpread(true);
        menuVo.setChildren(getTree(menus, true));
        menuVos.add(menuVo);
        return menuVos;
    }

    @Override
    public List<MenuVo> getMenuTreeList(String userId) {
        List<Menu> list = getPermissions(userId);
        return getTree(list, true);
    }

    /**
     * 获取菜单树
     * type = true 递归遍历到菜单
     * type = false 递归遍历到按钮
     * @param menus
     * @param type
     * @return
     */
    private List<MenuVo> getTree(List<Menu> menus, boolean type) {
        List<MenuVo> list = new ArrayList<>();
        if (CollectionUtils.isEmpty(menus)) {
            return list;
        }
        for (Menu menu : menus) {
            if (menu.getParentId().equals("0")) {
                MenuVo vo = new MenuVo();
                vo.setMenuId(menu.getMenuId());
                vo.setMenuName(menu.getMenuName());
                vo.setMenuIcon(menu.getMenuIcon());
                vo.setMenuUrl(menu.getMenuUrl());
                vo.setParentId(menu.getParentId());
                vo.setMenuType(menu.getMenuType());
                if (type) {
                    vo.setChildren(getChildExceptButton(menu.getMenuId(), menus));
                } else {
                    vo.setChildren(getAllChild(menu.getMenuId(), menus));
                }
                list.add(vo);
            }
        }
        return list;
    }

    /**
     * 递归遍历所有数据
     * @param menuId
     * @param menus
     * @return
     */
    private List<MenuVo> getAllChild(String menuId, List<Menu> menus) {
        List<MenuVo> list = new ArrayList<>();
        for (Menu menu : menus) {
            if (menu.getParentId().equals(menuId)) {
                MenuVo vo = new MenuVo();
                vo.setMenuId(menu.getMenuId());
                vo.setMenuName(menu.getMenuName());
                vo.setMenuIcon(menu.getMenuIcon());
                vo.setMenuUrl(menu.getMenuUrl());
                vo.setParentId(menu.getParentId());
                vo.setMenuType(menu.getMenuType());
                vo.setChildren(getAllChild(menu.getMenuId(), menus));
                list.add(vo);
            }
        }
        return list;
    }

    /**
     * 只递归到菜单（不包含按钮）
     * @param menuId
     * @param menus
     * @return
     */
    private List<MenuVo> getChildExceptButton(String menuId, List<Menu> menus) {
        List<MenuVo> list = new ArrayList<>();
        for (Menu menu : menus) {
            if (Objects.equals(menu.getParentId(), menuId) && menu.getMenuType() != 3) {
                MenuVo vo = new MenuVo();
                vo.setMenuId(menu.getMenuId());
                vo.setMenuName(menu.getMenuName());
                vo.setMenuIcon(menu.getMenuIcon());
                vo.setMenuUrl(menu.getMenuUrl());
                vo.setParentId(menu.getParentId());
                vo.setMenuType(menu.getMenuType());
                vo.setChildren(getChildExceptButton(vo.getMenuId(), menus));
                list.add(vo);
            }
        }
        return list;
    }

    /**
     * 根据用户ID查询菜单权限
     * 根据用户查询拥有的权限
     * 先查出用户拥有的角色
     * 再去查用户拥有的权限
     * 也可以多表关联查询
     * @param userId
     * @return
     */
    public List<Menu> getPermissions(String userId) {
        List<String> roleIds = userRoleService.getRoleIdsByUserId(userId);

        if (CollectionUtils.isEmpty(roleIds)) {
            return null;
        }

        List<Object> permissionIds = roleMenuService.listObjs(Wrappers.<RoleMenu>lambdaQuery().select(RoleMenu::getMenuId).in(RoleMenu::getRoleId, roleIds));
        if (CollectionUtils.isEmpty(permissionIds)) {
            return null;
        }

        LambdaQueryWrapper<Menu> wrapper = Wrappers.<Menu>lambdaQuery().in(Menu::getMenuId, permissionIds).orderByAsc(Menu::getMenuOrder);
        return menuMapper.selectList(wrapper);
    }

    /**
     * 验证菜单
     * 操作后的菜单类型是目录的时候 父级必须为目录
     * 操作后的菜单类型是菜单的时候 父类必须为目录类型
     * 操作后的菜单类型是按钮的时候 父类必须为菜单类型
     * @param menu
     */
    private void verifyMenu(Menu menu) {
        Menu parent;
        parent = menuMapper.selectById(menu.getParentId());
        switch (menu.getMenuType()) {
            case 1:
                if (Objects.nonNull(parent)) {
                    if (!Objects.equals(parent.getParentId(), "1")) {
                        throw new BusinessException(BaseResponseCode.OPERATION_MENU_PERMISSION_CATALOG_ERROR);
                    }
                } else if (!Objects.equals(parent.getParentId(), "0")) {
                    throw new BusinessException(BaseResponseCode.OPERATION_MENU_PERMISSION_CATALOG_ERROR);
                }
                break;

            case 2:
                if (Objects.isNull(parent) || !Objects.equals(parent.getMenuType(), "1")) {
                    throw new BusinessException(BaseResponseCode.OPERATION_MENU_PERMISSION_MENU_ERROR);
                }
                if (StringUtils.isEmpty(menu.getMenuUrl())) {
                    throw new BusinessException(BaseResponseCode.OPERATION_MENU_PERMISSION_URL_NOT_NULL);
                }
                break;

            case 3:
                if (Objects.isNull(parent) || !Objects.equals(parent.getMenuType(), "2")) {
                    throw new BusinessException(BaseResponseCode.OPERATION_MENU_PERMISSION_BTN_ERROR);
                }
                if (StringUtils.isEmpty(menu.getMenuPermission())) {
                    throw new BusinessException(BaseResponseCode.OPERATION_MENU_PERMISSION_URL_PERMS_NULL);
                }
                if (StringUtils.isEmpty(menu.getMenuUrl())) {
                    throw new BusinessException(BaseResponseCode.OPERATION_MENU_PERMISSION_URL_NOT_NULL);
                }
                break;
            default:
        }
    }

}
